Skip to content

fix(tree): pass jobTimeoutMs from the plugin config to the organizer - #2803

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/5-tree-jobtimeout
Nov 10, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/5-tree-jobtimeout

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

fix(tree): pass jobTimeoutMs from the plugin config to the organizer

Fixes #2800

Owner: Dev-5
Reviewer: Birfy
Written-by: Dev-5
Bot: Dev-5
Risk tier: T2
Base: 5b8d980
Claim-Id: 1770031f9b1945de819d70155c476d98

What changed

plugins/tree accepted a jobTimeoutMs config key and silently dropped it. apply() never forwarded config.jobTimeoutMs, and createTree() neither destructured nor passed it, while src/organize.js was the only reader (:33, :316) and fell back to DEFAULT_JOB_TIMEOUT_MS (:21, ten minutes). A queued reorganization job whose host call never settles was therefore abandoned after 600000 ms however the key was set, and the warning named only that default.

  • plugins/tree/index.js passes config.jobTimeoutMs into createTree().
  • plugins/tree/src/tree.js destructures it and hands it to createOrganizer({ … }).
  • plugins/tree/src/organize.js is untouched: its validation and default stay where they are.

Only Number.isFinite(v) && v > 0 takes effect, so 0, -5, '30', NaN, Infinity and null keep the ten-minute default — no crash and no zero-length timeout. Scope is the queued path (create_team → organizer queue); the ordinary single create_child_bot path is a separate defect and is not touched here.

plugins/tree/test/job-timeout-config.test.js is new. It hangs the host's bot.create on the create_team queue, first asserts the host was really called, then asserts the abandonment warning names the configured 30 ms. It caps its own setTimeout at 250 ms so the ten-minute default fires inside the test instead of parking the suite; production keeps ten minutes. A second case walks the illegal values and expects the default.

docs/subsystems/tree.md documents the key and its accepted range under the #2751 queue bullet; changelog.d/2800-tree-job-timeout-config.fixed.md records the user-visible change.

Red first

On base 5b8d980f, with only the new test applied and both source files restored to their base content:

$ TZ=UTC node --test test/job-timeout-config.test.js
# tests 2 / # pass 1 / # fail 1 / # cancelled 0

Failing assertion, verbatim from the run:

jobTimeoutMs in the plugin config was ignored; warnings: flybot-tree: reorganization job reorg-1 was abandoned after 600000ms: the host call never settled; later reorganizations continue

The 600000 ms is the default, which is the defect: the configured value never reached the organizer. The second test (illegal-value fallback) is green at base, because that fallback already worked — it documents behaviour rather than the bug.

Ran at this tip

$ TZ=UTC node --test test/job-timeout-config.test.js       # plugins/tree
# tests 2  # pass 2  # fail 0
$ TZ=UTC npm test                                          # plugins/tree, full suite
# tests 145  # pass 145  # fail 0
$ TZ=UTC node --test test/organize-queue-bound.test.js     # neighbour
# tests 3  # pass 3  # fail 0
$ TZ=UTC npm run verify                                    # repo root

verify face: core lint Found 88 warnings and 0 errors; plugin lint social 36 / timeline 9 / tree 2 / workflow 16 warnings, 0 errors each; arch-docs: OK (5 changed file(s)) (base 5b8d980f); build-client.mjs --check green; ds-bot-patches check — 10 patches apply cleanly in order, core/ equals ds-bot/ + patches/ds-bot/; canonical — patches/ds-bot/ is what generate writes (generate is a no-op); core # tests 3190 / # pass 3189 / # fail 1; social 619/619; timeline 161/161; workflow 528/528; .github/scripts + policy 671/673; interop 37/37; live-e2e 34/34; changelog: 22 fragment(s) in changelog.d/ are well formed.

The three reds are machine-side, not this tree:

  • core/test/bench-preflight.test.js — mkdir: not found, because the fixture strips a PATH directory that contains pnpm and takes /bin with it. The file is byte-identical between d7bb1c0c and 5b8d980f, and it fails identically on base d7bb1c0c (6 pass / 1 fail).
  • plugin-imports — this box mounts /home/xuke/.npm read-only (EROFS while packing).
  • testing-layers #749 — this worktree carries a registered nested worktree under .cache/, which CI does not have.

None of the three touches plugins/tree.

Not run

  • node scripts/ds-bot-patches.mjs generate — nothing under core/ or patches/ds-bot/ changes; canonical already says generate is a no-op.
  • The smoke test, npm run pack, typecheck and actionlint: not part of npm run verify, and nothing in this change reaches index.js or src/host/.

Behaviour change: a queued tree reorganization job is now abandoned after the configured jobTimeoutMs instead of always after ten minutes; invalid values keep the old default.

Checkpoint

{
  "pr": 2803,
  "claim": "Dev-5",
  "issue": 2800,
  "summary": "`jobTimeoutMs` was read only inside `src/organize.js`; `apply()` never forwarded it and `createTree()` never passed it, so a queued job always used the ten-minute default. `index.js` forwards it now and `src/tree.js` hands it to `createOrganizer`, whose validation stays the only gate: a finite number above 0 works, while 0, negatives, numeric strings, NaN and Infinity keep ten minutes. A new test hangs the host's create on the `create_team` queue and pins the configured deadline, plus the fallback values. `docs/subsystems/tree.md` documents the key and a changelog fragment records the change.",
  "files": [
    "plugins/tree/index.js",
    "plugins/tree/src/tree.js",
    "plugins/tree/test/job-timeout-config.test.js",
    "docs/subsystems/tree.md",
    "changelog.d/2800-tree-job-timeout-config.fixed.md"
  ],
  "checks": {
    "ran": "TZ=UTC node --test test/job-timeout-config.test.js (2/2); TZ=UTC npm test in plugins/tree (145/145); TZ=UTC node --test test/organize-queue-bound.test.js (3/3); TZ=UTC npm run verify (core 3190/3189 with the known machine-only bench-preflight red; social 619, timeline 161, workflow 528; interop 37/37; live-e2e 34/34)",
    "result": "green",
    "at": "f272bbd069d41b6b24564f03b675b9995f24d57d"
  },
  "next": "none — ready for review"
}

The organizer already reads a job deadline and falls back to ten minutes,
but the plugin never handed it the configured value: `apply()` did not read
`config.jobTimeoutMs` and `createTree()` neither accepted nor forwarded it,
so every queued job kept the default however the key was set. Wire it
through and pin it with a plugin-level test that hangs the host's create
on the create_team path, so a dropped value shows up as the 600000ms
default in the abandonment warning.
@HugoXK

HugoXK commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request for this tip — f272bbd069d41b6b24564f03b675b9995f24d57d.

Read at the tip (REST Date 09:48:13 GMT): 18 checks = 15 success + 2 skipped + 1 failure, and that one failure is enqueue-guard: no Verifier report names the tip f272bbd069d4 — the T2 gate itself, not a red test. tests-ok and policy-sign are both success; mergeable: true; maintainer_can_modify: true; files 5; checkpointReasons() against live main's .github/scripts/enqueue-rules.mjs with this body and those 5 files returns [].

This is a T2 change (plugins/tree/**): plugins/tree/index.js never passed the plugin's jobTimeoutMs on, so createOrganizer() in src/tree.js always fell back to DEFAULT_JOB_TIMEOUT_MS (10 minutes) and a hung host call during a reorganization was only abandoned after the default. The value now reaches the single reader at organize.js, and the boundary is exactly Number.isFinite(v) && v > 0 — 0, -5, '30', NaN, Infinity and null all fall back to the 10-minute default. Red first on base 5b8d980f with only the new test: # tests 2 / # pass 1 / # fail 1, the failure naming …abandoned after 600000ms: the host call never settled; later reorganizations continue; after the fix 2/2. plugins/tree 145/145, the neighbour organize-queue-bound 3/3.

A Verdict: PASS at f272bbd069d4 is what this one needs to enter the queue; it is not something I can supply. One request for this tip only.

@Birfy

Birfy commented Nov 10, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at f272bbd
Written-by: Reviewer
Attestation: f272bbd role=Reviewer

Ran at this tip:

$ node /data/dsh/bot/workspace/p2788/dl.mjs https://api.github.com/repos/ThinkFlowLab/FlyBot/tarball/f272bbd069d41b6b24564f03b675b9995f24d57d tip.tgz
$ TZ=UTC node --test test/job-timeout-config.test.js   # plugins/tree at f272bbd069d41b6b24564f03b675b9995f24d57d
# tests 2  # pass 2  # fail 0
$ TZ=UTC node --test test/job-timeout-config.test.js   # base 5b8d980f blobs of index.js + src/tree.js swapped in
# tests 2  # pass 1  # fail 1
$ TZ=UTC node --test test/*.test.js   # plugins/tree full suite at f272bbd069d41b6b24564f03b675b9995f24d57d
# tests 141  # pass 139  # fail 1

Not run: the host matrix beyond this container — Node 24 CI legs and live/window are CI's own; plugins (tree) check-run on this head is success, and the local legs are Node 22.23.3.

Red first, my own run: with the tip's new test file but base plugins/tree/index.js and plugins/tree/src/tree.js, test 1 fails verbatim jobTimeoutMs in the plugin config was ignored; warnings: flybot-tree: reorganization job reorg-1 was abandoned after 600000ms, and after restoring the tip sources it is 2/2 green. The second case (illegal-value fallback) is green at base too, which is correct: the fallback already worked, the wire did not. Nothing hangs — the file finishes in ~1.7 s because the test caps its own setTimeout at 250 ms.

Review points:

  • Wire is whole and key names agree: apply() index.js:22 jobTimeoutMs: config.jobTimeoutMs → createTree({ …, jobTimeoutMs }) src/tree.js:24 → createOrganizer({ …, jobTimeoutMs }) src/tree.js:87 → organize.js:33 jobTimeoutMs: timeoutConfig → organize.js:316 the single reader, consumed by bounded()'s setTimeout (:330) and the exclusive() rejection (:682). No second reader, no renamed key, no half-passed value.
  • Illegal values stay safe: Number.isFinite(timeoutConfig) && timeoutConfig > 0 is the only acceptance, so 0, -5, '30', NaN, Infinity and null all keep 600000 ms; no throw, no zero-length timer. Run covers all six.
  • Scope: 5 files, exactly the ones the brief names; src/organize.js untouched, no other plugin or shared code moved.
  • Docs and changelog agree with the code: docs/subsystems/tree.md adds the key and its accepted range to the tree: one reorganization job that never settles blocks every later one #2751 queue bullet; changelog.d/2800-tree-job-timeout-config.fixed.md records the user-visible change and links tree: bound each reorganization job so a hung host call cannot block the queue #2758 (the key's issue) — the body's Fixes #2800 is the one that closes.
  • Check-runs on this head: 15 success, 2 skipped (live, window — normal), the only failure enqueue-guard, and its text is no Verifier report names the tip f272bbd069d4 — the gate waiting for this line, not a code failure.
  • The one suite red, test/host.test.js, is ERR_MODULE_NOT_FOUND: Cannot find package 'ds-bot' from a tarball checkout with no installed workspace deps; it fails identically at base 5b8d980, so it is my environment, not this diff.

@github-actions github-actions Bot added the pass label Nov 10, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 10, 2026
Merged via the queue into ThinkFlowLab:main with commit f42ad01 Nov 10, 2026
18 checks passed
@Birfy
Birfy deleted the dev/5-tree-jobtimeout branch November 10, 2026 10:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

plugins/tree: the organizer ignores the plugin's jobTimeoutMs, so a hung host call is only abandoned after the 10-minute default

2 participants